#include "CvUtil.h"
#include <fstream>
bool CvUtil::imwrite_unicode(const std::filesystem::path &p, const cv::Mat &img, const std::vector<int> &params)
{
    try
    {
        // 1. 检查输入图像是否为空
        if (img.empty())
        {
            return false;
        }

        // 2. 从路径中获取文件扩展名 (例如 ".jpg")
        std::string ext = p.extension().string();
        if (ext.empty())
        {
            return false;
        }

        // 3. 将图像编码到内存缓冲区
        std::vector<uchar> buffer;

        if (!cv::imencode(ext, img, buffer, params) || buffer.empty())
        {
            return false;
        }

        // 4. 使用 C++ 标准库将缓冲区数据写入文件
        std::ofstream ofs(p, std::ios::binary);
        if (!ofs.is_open())
        {
            return false;
        }
        ofs.write(reinterpret_cast<const char *>(buffer.data()), buffer.size());
        ofs.close();
    }
    catch (...)
    {
        return false;
    }
    return true;
}

bool CvUtil::imread_unicode(const std::filesystem::path &p, cv::Mat &img, int flags)
{
    try
    {
        // 1. 检查文件是否存在
        if (!std::filesystem::exists(p) || !std::filesystem::is_regular_file(p))
        {
            return false;
        }

        // 2. 获取文件大小
        std::uintmax_t fileSize = std::filesystem::file_size(p);
        if (fileSize == 0)
        {
            return false;
        }

        // 3. 使用C++标准库读取文件内容到内存缓冲区
        std::ifstream ifs(p, std::ios::binary);
        if (!ifs.is_open())
        {
            return false;
        }

        std::vector<uchar> buffer(fileSize);
        ifs.read(reinterpret_cast<char *>(buffer.data()), buffer.size());
        ifs.close();

        // 4. 使用cv::imdecode将缓冲区数据解码为cv::Mat
        img = cv::imdecode(buffer, flags);

        // 5. 检查解码是否成功
        return !img.empty();
    }
    catch (...)
    {
        return false;
    }
}

cv::Mat CvUtil::cropBinaryAuto(const cv::Mat &img)
{
    // 1. 检查输入图像是否为空
    if (img.empty())
    {
        return cv::Mat();
    }
    // 2. 检查输入图像是否为二值图像
    if (img.type() != CV_8UC1)
    {
        return cv::Mat();
    }
    // 3. 查找非零像素的最小和最大行索引
    int minRow = img.rows;
    int maxRow = 0;
    for (int i = 0; i < img.rows; i++)
    {
        for (int j = 0; j < img.cols; j++)
        {
            if (img.at<uchar>(i, j) != 0)
            {
                minRow = std::min(minRow, i);
                maxRow = std::max(maxRow, i);
                break;
            }
        }
    }
    // 4. 查找非零像素的最小和最大列索引
    int minCol = img.cols;
    int maxCol = 0;
    for (int i = 0; i < img.cols; i++)
    {
        for (int j = 0; j < img.rows; j++)
        {
            if (img.at<uchar>(j, i) != 0)
            {
                minCol = std::min(minCol, i);
                maxCol = std::max(maxCol, i);
                break;
            }
        }
    }
    // 5. 检查是否找到有效区域
    if (minRow > maxRow || minCol > maxCol)
    {
        return cv::Mat();
    }
    // 6. 提取有效区域
    cv::Rect roi(minCol, minRow, maxCol - minCol + 1, maxRow - minRow + 1);
    cv::Mat croppedImg = img(roi);

    return croppedImg;
}

cv::Mat CvUtil::colorBinary(const cv::Mat &image, const cv::Vec3b &targetColor, int tolerance)
{
    // 参数验证
    if (image.empty())
    {
        // std::cerr << "错误: 输入图像为空!" << std::endl;
        return cv::Mat();
    }

    if (tolerance < 0)
    {
        // std::cerr << "警告: 容差不能为负数，已自动调整为0" << std::endl;
        tolerance = 0;
    }

    if (tolerance > 100)
    {
        // std::cerr << "警告: 容差过大可能导致颜色选择不准确" << std::endl;
    }

    // 将目标BGR颜色转换为HSV
    cv::Mat targetMat(1, 1, CV_8UC3, targetColor);
    cv::Mat targetMatHSV;
    cv::cvtColor(targetMat, targetMatHSV, cv::COLOR_BGR2HSV);
    cv::Vec3b targetHSV = targetMatHSV.at<cv::Vec3b>(0, 0);

    // 将输入图像转换为HSV
    cv::Mat hsvImage;
    cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);

    // 计算HSV各通道的阈值范围
    // 注意: OpenCV中H范围是0-180, S和V范围是0-255
    int h_low = std::max(0, static_cast<int>(targetHSV[0]) - tolerance);
    int h_high = std::min(180, static_cast<int>(targetHSV[0]) + tolerance);

    // 对于饱和度和明度，使用稍大的容差系数，因为它们对光照变化更敏感
    int s_tolerance = static_cast<int>(tolerance * 1.2);
    int v_tolerance = static_cast<int>(tolerance * 1.2);

    int s_low = std::max(0, static_cast<int>(targetHSV[1]) - s_tolerance);
    int s_high = std::min(255, static_cast<int>(targetHSV[1]) + s_tolerance);

    int v_low = std::max(0, static_cast<int>(targetHSV[2]) - v_tolerance);
    int v_high = std::min(255, static_cast<int>(targetHSV[2]) + v_tolerance);

    // 创建二值掩码
    cv::Mat mask;
    cv::inRange(hsvImage,
                cv::Scalar(h_low, s_low, v_low),
                cv::Scalar(h_high, s_high, v_high),
                mask);

    return mask;
}

cv::Mat CvUtil::colorBinaryAdvanced(const cv::Mat &image, const cv::Vec3b &targetColor,
                                    int h_tolerance, int s_tolerance, int v_tolerance)
{
    if (image.empty())
    {
        std::cerr << "错误: 输入图像为空!" << std::endl;
        return cv::Mat();
    }

    // 将目标BGR颜色转换为HSV
    cv::Mat targetMat(1, 1, CV_8UC3, targetColor);
    cv::Mat targetMatHSV;
    cv::cvtColor(targetMat, targetMatHSV, cv::COLOR_BGR2HSV);
    cv::Vec3b targetHSV = targetMatHSV.at<cv::Vec3b>(0, 0);

    // 将输入图像转换为HSV
    cv::Mat hsvImage;
    cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);

    // 计算各通道阈值范围
    int h_low = std::max(0, static_cast<int>(targetHSV[0]) - h_tolerance);
    int h_high = std::min(180, static_cast<int>(targetHSV[0]) + h_tolerance);

    int s_low = std::max(0, static_cast<int>(targetHSV[1]) - s_tolerance);
    int s_high = std::min(255, static_cast<int>(targetHSV[1]) + s_tolerance);

    int v_low = std::max(0, static_cast<int>(targetHSV[2]) - v_tolerance);
    int v_high = std::min(255, static_cast<int>(targetHSV[2]) + v_tolerance);

    // 创建二值掩码
    cv::Mat mask;
    cv::inRange(hsvImage,
                cv::Scalar(h_low, s_low, v_low),
                cv::Scalar(h_high, s_high, v_high),
                mask);

    return mask;
}

cv::Rect CvUtil::findConnectedRectangles(const std::vector<std::vector<cv::Rect>> &results)
{
    if (results.empty())
    {
        return cv::Rect(-1, -1, -1, -1);
    }

    // 用于存储当前列找到的候选矩形及其路径
    struct Candidate
    {
        cv::Rect rect;
        std::vector<cv::Rect> path;
    };

    // 初始化第一列的候选
    std::vector<Candidate> currentCandidates;
    for (const auto &rect : results[0])
    {
        currentCandidates.push_back({rect, {rect}});
    }

    // 如果第一列没有矩形，直接返回失败
    if (currentCandidates.empty())
    {
        return cv::Rect(-1, -1, -1, -1);
    }

    // 遍历后续的列
    for (size_t col = 1; col < results.size(); ++col)
    {
        const auto &currentRects = results[col];

        // 如果当前列为空，直接返回失败
        if (currentRects.empty())
        {
            return cv::Rect(-1, -1, -1, -1);
        }

        std::vector<Candidate> nextCandidates;

        // 对每个候选路径，在当前列查找满足条件的矩形
        for (const auto &candidate : currentCandidates)
        {
            const cv::Rect &prevRect = candidate.rect;

            // 计算允许的距离阈值
            // int allowedDistanceX = prevRect.width / 2;
            // int allowedDistanceY = prevRect.height / 2;

            int allowedDistanceX = prevRect.width * 4;
            int allowedDistanceY = prevRect.height;

            // 在当前列查找满足条件的矩形
            for (const auto &currentRect : currentRects)
            {
                // 检查第二个矩形是否在第一个矩形的右边
                if (currentRect.x <= prevRect.x)
                {
                    continue;
                }

                // 检查水平间距
                int horizontalGap = currentRect.x - (prevRect.x + prevRect.width);
                if (horizontalGap > allowedDistanceX)
                {
                    continue;
                }

                // 检查垂直对齐（允许一定的垂直偏移）
                int verticalCenter1 = prevRect.y + prevRect.height / 2;
                int verticalCenter2 = currentRect.y + currentRect.height / 2;
                if (std::abs(verticalCenter1 - verticalCenter2) > allowedDistanceY)
                {
                    continue;
                }

                // 找到满足条件的矩形，创建新的候选
                Candidate newCandidate = candidate;
                newCandidate.rect = currentRect;
                newCandidate.path.push_back(currentRect);
                nextCandidates.push_back(newCandidate);

                // 找到第一个满足条件的就跳出，避免不必要的检查
                break;
            }
        }

        // 如果没有找到满足条件的候选，返回失败
        if (nextCandidates.empty())
        {
            return cv::Rect(-1, -1, -1, -1);
        }

        currentCandidates = std::move(nextCandidates);
    }

    // 成功找到完整的路径，计算包围所有矩形的边界框
    if (!currentCandidates.empty())
    {
        const auto &path = currentCandidates[0].path;

        int minX = INT_MAX, minY = INT_MAX;
        int maxX = 0, maxY = 0;

        for (const auto &rect : path)
        {
            minX = std::min(minX, rect.x);
            minY = std::min(minY, rect.y);
            maxX = std::max(maxX, rect.x + rect.width);
            maxY = std::max(maxY, rect.y + rect.height);
        }

        return cv::Rect(minX, minY, maxX - minX, maxY - minY);
    }

    return cv::Rect(-1, -1, -1, -1);
}
std::vector<std::wstring> CvUtil::sortOcrResults(const std::vector<OcrResult> &ocrResults, std::vector<cv::Rect> &rects)
{
    std::vector<std::wstring> sortedTexts;
    rects.clear();

    if (ocrResults.empty())
    {
        return sortedTexts;
    }

    // 创建可修改的副本用于排序
    std::vector<OcrResult> sortedResults = ocrResults;

    // 自定义比较函数：从上到下，从左到右排序
    auto compareOcrResults = [](const OcrResult &a, const OcrResult &b)
    {
        // 首先按Y坐标（从上到下）排序
        // 如果两个矩形在同一行（Y坐标差异小于矩形高度的1/3），则按X坐标（从左到右）排序
        int yThreshold = std::min(a.rect_.height, b.rect_.height) / 3;

        if (std::abs(a.rect_.y - b.rect_.y) <= yThreshold)
        {
            // 在同一行，按X坐标排序
            return a.rect_.x < b.rect_.x;
        }
        else
        {
            // 不在同一行，按Y坐标排序
            return a.rect_.y < b.rect_.y;
        }
    };

    // 对结果进行排序
    std::sort(sortedResults.begin(), sortedResults.end(), compareOcrResults);

    // 提取排序后的文本和矩形框
    for (const auto &result : sortedResults)
    {
        sortedTexts.push_back(result.name_);
        rects.push_back(result.rect_);
    }

    return sortedTexts;
}
